package ru.pbem.olympia.utils;

import java.util.*;

/**
 * User: Roman Pavlov
 * Date: 15-Sep-2010
 * Time: 15:10:06
 */
public class MapHierarchy<K, V> implements Hierarchy<K, V> {
    private final Map<K, HierarchyEntry<K, V>> map;

    private static final String MESSAGE_NODE_WITH_KEY_DOES_NOT_EXIST = "Node with key %s does not exist";
    private static final String MESSAGE_NODE_WITH_KEY_ALREADY_EXISTS = "Node with key %s already exists";

    public MapHierarchy() {
        this.map = new HashMap<K, HierarchyEntry<K, V>>(1000);
    }

    public boolean removeNode(final K removeNode) {
        if (this.map.containsKey(removeNode)) {
            final Hierarchy.Entry<K, V> entry = this.map.remove(removeNode);
            final List<K> children = entry.getChildren();
            for (final K childKey : children) {
                removeNode(childKey);
            }
            return true;
        }
        return false;
    }

    public void addNode(final K key, final V value, final K addToNode) {
        checkNotExists(key);
        if (addToNode != null){
            checkExists(addToNode);
            this.map.get(addToNode).addChild(key);
        }
        this.map.put(key, new HierarchyEntry<K, V>(key, value, addToNode, null));
    }

    public Hierarchy.Entry<K, V> getNode(final K key) {
        return this.map.get(key);
    }

    public void moveNode(final K key, final K newRoot) {

    }

    private void checkNotExists(final K key) {
        if (!this.map.containsKey(key)) {
            throw new IllegalArgumentException(String.format(MapHierarchy.MESSAGE_NODE_WITH_KEY_ALREADY_EXISTS, key));
        }
    }

    private void checkExists(final K key) {
        if (this.map.containsKey(key)) {
            throw new IllegalArgumentException(String.format(MapHierarchy.MESSAGE_NODE_WITH_KEY_DOES_NOT_EXIST, key));
        }
    }

    @Override
    public String toString() {
        return "MapHierarchy{" +
                "map=" + this.map +
                '}';
    }

    public class HierarchyEntry<K, V> implements Hierarchy.Entry<K, V> {
        private final K key;
        private V value;
        private K parent;
        private List<K> children;

        private HierarchyEntry(final K key, final V value, final K parent, final List<K> children) {
            this.key = key;
            this.value = value;
            this.parent = parent;
            this.children = children == null ? new ArrayList<K>(0) : children;
        }

        public K getKey() {
            return this.key;
        }

        public V getValue() {
            return this.value;
        }

        public K getParent() {
            return this.parent;
        }

        public List<K> getChildren() {
            return Collections.unmodifiableList(this.children);
        }

        private boolean addChild(final K childKey) {
            return children.add(childKey);
        }

        private boolean removeChild(final K childKey) {
            return children.remove(childKey);
        }

        @Override
        public String toString() {
            return "HierarchyEntry{" +
                    "key=" + this.key +
                    ", value=" + this.value +
                    ", parent=" + this.parent +
                    ", children=" + this.children +
                    '}';
        }
    }
}
